import { LogStatus, LogTypes } from "../common/enums"
import { asyncForEach } from "../common/asyncForEach"
import { StepExecutor } from "./stepExecutor"
import { listenPageRequest } from "../common/listenPageRequest"
import { listenPageError } from "../common/listenPageError"
import { BROWSER_WIDTH, BROWSER_HEIGHT } from "../common/consts"
import { extractError } from "../common/extractError"
import { installMouseHelper } from './installMouseHelper'
import { capturePage } from "../common/capturePage"

const ifStepAssertEqualToBaseEnv = step => step.data.assertTypeCode === 'equalToBaseEnv' && step.code === 'assert'

const doRunSuite = async ({ targetOrBase, page, onAddLog, onFinishAssertBaseEnvStep, env, captureScreenShot, retryTimesOnError, planId, suite }) => {
    const runningOnTarget = targetOrBase === 'target'
    const { id: suiteId, stepList } = suite
    const start = +new Date()

    // 监听网络请求
    await listenPageRequest(page, planId, suiteId)

    // 监听页面js错误
    let params = null
    runningOnTarget && listenPageError(page, error => {
        params = [LogTypes.js_error, { planId, suiteId, status: '', date: start, startEqualToBaseEnvSuite: false, error }]
        onAddLog(params)
    })

    // start suite
    let status = LogStatus.start

    let assertEqualToBaseEnvStepIds = stepList.filter(step => ifStepAssertEqualToBaseEnv(step)).map(step => step.id)
    params = [LogTypes.suite, { planId, suiteId, status: LogStatus.start, date: start, startEqualToBaseEnvSuite: !!assertEqualToBaseEnvStepIds.length }]
    onAddLog(params)

    let anyStepFailure = false

    await asyncForEach(stepList, async (step, index) => {
        // await page.waitFor(2000)
        const stepId = step.id

        const assertEqualToBaseEnv = ifStepAssertEqualToBaseEnv(step)

        // start
        const start = +new Date()
        params = [LogTypes.step, { planId, suiteId, stepId, status: LogStatus.start, date: start, step, result: {} }]
        onAddLog(params)

        // execute
        let lastError = null
        let screenshot = ''
        let result = {}
        let retryTimes = -1
        while (retryTimes++ < retryTimesOnError) {
            const resultWrapper = await StepExecutor({
                planId, suiteId, page, step, opts: {
                    env,
                    // 下一步是断言，此步骤也不会截屏
                    screenshot: captureScreenShot && ((index + 1 === stepList.length) || (stepList[index + 1].code !== 'assert'))
                }
            })
            const { error } = resultWrapper
            if (error) {
                // 运行出错，记录报错，判断重试次数
                lastError = (retryTimes ? `[已重试${retryTimes}次]` : '') + error
                screenshot = await capturePage(page)
            } else {
                lastError = resultWrapper.error
                screenshot = resultWrapper.screenshot
                result = resultWrapper.result
                break
            }
        }

        status = lastError || (step.code === 'assert' && !result.valid) ? LogStatus.failure : (assertEqualToBaseEnv ? LogStatus.pending : LogStatus.pass)

        // end
        const end = +new Date()
        params = [LogTypes.step, { planId, suiteId, stepId, status, date: end, elapse: end - start, step, result, screenshot, error: extractError(lastError) }]
        onAddLog(params)

        // 如果当前步骤是同基准环境的比较，完成该步骤就要通知到外部，当该步骤在测试环境和基准环境都完成，就实际完成该步骤
        assertEqualToBaseEnv && onFinishAssertBaseEnvStep({ targetOrBase, suiteId, result, screenshot, step, relativeStepIds: assertEqualToBaseEnvStepIds, logParams: [...params] })

        // refresh suite status
        anyStepFailure = anyStepFailure || status === LogStatus.failure
    })

    // end suite
    const end = +new Date()
    status = assertEqualToBaseEnvStepIds.length ? LogStatus.pending : (anyStepFailure ? LogStatus.failure : LogStatus.pass)
    params = [LogTypes.suite, { planId, suiteId, status, date: end, elapse: end - start }]

    if (!assertEqualToBaseEnvStepIds.length) {
        onAddLog(params)
    }
}

export const runSuite = async ({ browser, onAddLog, onAddBaseEnvLog, onFinishAssertBaseEnvStep, env, baseEnv, captureScreenShot, retryTimesOnError, planId, suite }) => {
    // 创建隐身窗口context，此context跟原cookie不相关
    const genNewContextPage = async () => {
        const context = await browser.createIncognitoBrowserContext()
        const page = await context.newPage()
        await page.setViewport({ width: BROWSER_WIDTH, height: BROWSER_HEIGHT })

        return page
    }

    const { requireBaseEnv } = suite

    if (requireBaseEnv) {
        //  存在基准测试，就先跑一下基准环境
        const page = await genNewContextPage()
        await doRunSuite({ targetOrBase: 'base', page, onAddLog: onAddBaseEnvLog, onFinishAssertBaseEnvStep, env: baseEnv, captureScreenShot, retryTimesOnError, planId, suite })
    }

    const page = await genNewContextPage()
    await installMouseHelper(page)  //为了在截图中能显示鼠标定位
    await doRunSuite({ targetOrBase: 'target', page, onAddLog, onFinishAssertBaseEnvStep, env, captureScreenShot, retryTimesOnError, planId, suite })
    page.close({ runBeforeUnload: true })
}
